/*
*
* Poll
*
* This class represents an electoral subdivision
*
*/

class Poll extends PlotObject {
  
  // ----------------------------------------
  // attributes
  // ---------------------------------------- 
  
  // "non-metric" attributes -- neighbourhood data not used as a metric
  String name, pollid, shortName, psbWard, csbWard;
  ArrayList coords;
  ArrayList coordsScreen;
  
  // metric HashMap
  HashMap<String, Float> metricHash;
  
  // ------------------------------------------
  // constructor
  // ------------------------------------------
  Poll(String n, String pid, String sn, String psbw, String csbw, ArrayList cs) {
    name = n;
    pollid = pid;
    shortName = sn;
    psbWard = psbw;
    csbWard = csbw;
    coords = cs;
    coordsScreen = new ArrayList();
    metricHash = new HashMap<String, Float>();
    
    // populate metricHash, assigning each key the no data flag
    // "simple" attributes
    metricHash.put("DARYL BONAR", float(-1));
    metricHash.put("DAVID DORWARD", float(-1));
    metricHash.put("DAVE DOWLING", float(-1));
    metricHash.put("DAN DROMARSKY", float(-1));
    metricHash.put("BOB LIGERTWOOD", float(-1));
    metricHash.put("ANDREW LINEKER", float(-1));
    metricHash.put("STEPHEN MANDEL", float(-1));
    metricHash.put("ANDREW KNACK", float(-1));
    metricHash.put("JAMIE KENNETH POST", float(-1));
    metricHash.put("LINDA SLOAN", float(-1));
    metricHash.put("THOMAS HINDERKS", float(-1));
    metricHash.put("DON KOZIAK", float(-1));
    metricHash.put("KIM KRUSHELL", float(-1));
    metricHash.put("ROXIE MALONE-RICHARDS", float(-1));
    metricHash.put("SHELLEY TUPPER", float(-1));
    metricHash.put("MICHAEL WADDY", float(-1));
    metricHash.put("KIM CASSADY", float(-1));
    metricHash.put("TERRY DEMERS", float(-1));
    metricHash.put("SHAWN PHILIP FAIRBRIDGE", float(-1));
    metricHash.put("DAVE LOKEN", float(-1));
    metricHash.put("HATEM NABOULSI", float(-1));
    metricHash.put("JOHN (GIOVANNI) OPLANICH", float(-1));
    metricHash.put("GREG SIVER", float(-1));
    metricHash.put("LOUIS SOBOLEWSKI", float(-1));
    metricHash.put("MICHEAL SUESS", float(-1));
    metricHash.put("KEN ATKINSON", float(-1));
    metricHash.put("DAN BACKS", float(-1));
    metricHash.put("ED GIBBONS", float(-1));
    metricHash.put("HAFSA GOMA", float(-1));
    metricHash.put("SCOTT ROBB", float(-1));
    metricHash.put("STEVE BERGERON", float(-1));
    metricHash.put("MARK GRANDISH", float(-1));
    metricHash.put("BRIAN KENDRICK", float(-1));
    metricHash.put("KAREN LEIBOVICI", float(-1));
    metricHash.put("CRIS BASUALDO", float(-1));
    metricHash.put("JANE BATTY", float(-1));
    metricHash.put("CARLA FROST", float(-1));
    metricHash.put("JAMES JOHNSON", float(-1));
    metricHash.put("BRYAN GEORGE KAPITZA", float(-1));
    metricHash.put("LEE PERMANN", float(-1));
    metricHash.put("ADIL PIRBHAI", float(-1));
    metricHash.put("THOMAS ROBERTS", float(-1));
    metricHash.put("TONY CATERINA", float(-1));
    metricHash.put("SCOTT MCKEEN", float(-1));
    metricHash.put("GRANT DAVID PULLISHY", float(-1));
    metricHash.put("TERRY ROLLS", float(-1));
    metricHash.put("BRENDAN VAN ALSTINE", float(-1));
    metricHash.put("DUANE GOOD STRIKER", float(-1));
    metricHash.put("BEN HENDERSON", float(-1));
    metricHash.put("LORI JEFFERY-HEANEY", float(-1));
    metricHash.put("SHEILA MCKAY", float(-1));
    metricHash.put("HANA RAZGA", float(-1));
    metricHash.put("BRYAN KENT ANDERSON", float(-1));
    metricHash.put("RAMI BADER", float(-1));
    metricHash.put("CALVIN LIM", float(-1));
    metricHash.put("JENNIFER WATTS", float(-1));
    metricHash.put("DON IVESON", float(-1));
    metricHash.put("AL SLEMKO", float(-1));
    metricHash.put("SHANE BERGDAHL", float(-1));
    metricHash.put("KERRY DIOTTE", float(-1));
    metricHash.put("VISHAL LUTHRA", float(-1));
    metricHash.put("ROBERTO MAGLALANG", float(-1));
    metricHash.put("CHINWE OKELU", float(-1));
    metricHash.put("BRENT SCHAFFRICK", float(-1));
    metricHash.put("VIKRAM BAGGA", float(-1));
    metricHash.put("GERRY HORN", float(-1));
    metricHash.put("CHUCK MCKENNA", float(-1));
    metricHash.put("AMARJEET SOHI", float(-1));
    metricHash.put("BECKY KALLAL", float(-1));
    metricHash.put("ROMEO OCHOA", float(-1));
    metricHash.put("PATRICIA GRELL", float(-1));
    metricHash.put("LARRY KOWALCZYK", float(-1));
    metricHash.put("CARLA SMILEY", float(-1));
    metricHash.put("CYNTHIA BOSSMIN-DAVIS", float(-1));
    metricHash.put("JENNIFER CHAHAL", float(-1));
    metricHash.put("CINDY OLSEN", float(-1));
    metricHash.put("DEBBIE ENGEL", float(-1));
    metricHash.put("BRAD LOHNER", float(-1));
    metricHash.put("JOHN ACHESON", float(-1));
    metricHash.put("RUDY ARCILLA", float(-1));
    metricHash.put("LAURI FRIESEN", float(-1));
    metricHash.put("MARILYN BERGSTRA", float(-1));
    metricHash.put("BEV HEISLER", float(-1));
    metricHash.put("MICHAEL SAVARYN", float(-1));
    metricHash.put("KATHERINE SCHWABENBAUER", float(-1));
    metricHash.put("DANNY S. KINAL", float(-1));
    metricHash.put("KARA PELECH", float(-1));
    metricHash.put("LAURA THIBERT", float(-1));
    metricHash.put("CHERYL JOHNER", float(-1));
    metricHash.put("RORY JOHANNES KOOPMANS", float(-1));
    metricHash.put("KEVAN WARNER", float(-1));
    metricHash.put("AMANDA BEISIEGEL", float(-1));
    metricHash.put("PERRY CHAHAL", float(-1));
    metricHash.put("DARRYL LAGERQUIST", float(-1));
    metricHash.put("GEORGE P. NICHOLSON", float(-1));
    metricHash.put("KEN SHIPKA", float(-1));
    metricHash.put("MARY GRADISAR", float(-1));
    metricHash.put("TONY HENSHALL", float(-1));
    metricHash.put("CHRISTOPHER SPENCER", float(-1));
    metricHash.put("DAVE COLBURN", float(-1));
    metricHash.put("NEIL MACDONALD", float(-1));
    metricHash.put("HEATHER MACKENZIE", float(-1));
    metricHash.put("KEN SOROKA", float(-1));
    metricHash.put("MICHAEL JANZ", float(-1));
    metricHash.put("JOANNA ROZMUS", float(-1));
    metricHash.put("BEV SAWYER", float(-1));
    metricHash.put("SARAH HOFFMAN", float(-1));
    metricHash.put("GEORGE RICE", float(-1));
    metricHash.put("CATHERINE RIPLEY", float(-1));
    metricHash.put("LESLIE CLEARY", float(-1));
    metricHash.put("TINA JARDINE", float(-1));
    metricHash.put("BALRAJ MANHAS", float(-1));
    // non-name simple metrics
    metricHash.put("MAYOR OVER VOTES", float(-1));
    metricHash.put("MAYOR UNDER VOTES", float(-1));
    metricHash.put("COUNCILLOR OVER VOTES", float(-1));
    metricHash.put("COUNCILLOR UNDER VOTES", float(-1));
    metricHash.put("CATHOLIC SCHOOL BOARD TRUSTEE OVER VOTES", float(-1));
    metricHash.put("CATHOLIC SCHOOL BOARD TRUSTEE UNDER VOTES", float(-1));
    metricHash.put("PUBLIC SCHOOL BOARD TRUSTEE OVER VOTES", float(-1));
    metricHash.put("PUBLIC SCHOOL BOARD TRUSTEE UNDER VOTES", float(-1));
    metricHash.put("eligibleVoters", float(-1));
    // "calculated metrics"
    metricHash.put("totalMayorVotes", float(-1));
    metricHash.put("totalCouncilVotes", float(-1));
    metricHash.put("totalCSBVotes", float(-1));
    metricHash.put("totalPSBVotes", float(-1));
    metricHash.put("voterTurnout", float(-1));
    metricHash.put("area", float(-1));
    // 
    metricHash.put("mandelPercent", float(-1));
    
    // CONDITION POLYGON DATA USING DOUGLAS-PEUCKER METHOD
    
    float area = 0;
    
    for(int i = 0; i < coords.size(); i++) {
      
      PVector[] poly;
      
      // get polygon 
      poly = (PVector[]) coords.get(i);     
      
      // apply douglasPeucker method;
      // second argument is tunable "epsilon" value; higher values remove
      // more points
      poly = douglasPeucker(poly, 2);
      
      // calculate area of polygon and store in metricHash
      area += calculatePolygonArea(poly);
      metricHash.put("area", area);
      
      // store
      coords.remove(i);
      coords.add(i, poly);
      
      // convert coordinates to screen space and store in coordsxy arraylist
      PVector[] polyScreen = new PVector[poly.length];
      for(int j = 0; j < poly.length; j++) {
        polyScreen[j] = convUTMToScreen(poly[j]);
      } // for
      
      coordsScreen.add(polyScreen);

    } // for
    
    // assign noData flag to all attributes
          
  }  // constructor method

  // ---------------------------------------------
  // importElectionData method
  // takes election data array and assigns to attributes
  // ---------------------------------------------
  void importElectionData(String[] eda) {
    
    // check if eligibleVoters key in metricHash already populated; if not, populate it
    if((metricHash.get("eligibleVoters")) == -1) {
      metricHash.put("eligibleVoters", float(eda[7]));
    }
    // populate appropriate metricHash key with imported data
    String ke = split(eda[10], "\"")[1];
    float val = float(eda[11]);
    if(ke.equals("OVER VOTES") || ke.equals("UNDER VOTES")) {
      String[] kes = new String[2]; 
      kes[0] = split(eda[2], "\"")[1].toUpperCase();
      kes[1] = ke;
      ke = join(kes, " ");
    }

    metricHash.put(ke, val);
    
  } // importElectionData
  
  // ---------------------------------------------
  // genCalculatedMetrics method
  // generates "calculated" metrics using election data
  // imported with importElectionData method
  // ---------------------------------------------
  void genCalculatedMetrics() {
    
    // calculate metrics and store in metricHash
    
    // total votes
    float totalMayorVotes = (metricHash.get("DARYL BONAR")) +
                       (metricHash.get("DAVID DORWARD")) +
                       (metricHash.get("DAVE DOWLING")) +
                       (metricHash.get("DAN DROMARSKY")) +
                       (metricHash.get("BOB LIGERTWOOD")) +
                       (metricHash.get("ANDREW LINEKER")) +
                       (metricHash.get("STEPHEN MANDEL")) +
                       (metricHash.get("MAYOR OVER VOTES")) +
                       (metricHash.get("MAYOR UNDER VOTES"));
    metricHash.put("totalMayorVotes", totalMayorVotes);
    
    float totalCouncilVotes = (metricHash.get("ANDREW KNACK") +
                        metricHash.get("JAMIE KENNETH POST") +
                        metricHash.get("LINDA SLOAN") +
                        metricHash.get("THOMAS HINDERKS") +
                        metricHash.get("DON KOZIAK") +
                        metricHash.get("KIM KRUSHELL") +
                        metricHash.get("ROXIE MALONE-RICHARDS") +
                        metricHash.get("SHELLEY TUPPER") +
                        metricHash.get("MICHAEL WADDY") +
                        metricHash.get("KIM CASSADY") +
                        metricHash.get("TERRY DEMERS") +
                        metricHash.get("SHAWN PHILIP FAIRBRIDGE") +
                        metricHash.get("DAVE LOKEN") +
                        metricHash.get("HATEM NABOULSI") +
                        metricHash.get("JOHN (GIOVANNI) OPLANICH") +
                        metricHash.get("GREG SIVER") +
                        metricHash.get("LOUIS SOBOLEWSKI") +
                        metricHash.get("MICHEAL SUESS") +
                        metricHash.get("KEN ATKINSON") +
                        metricHash.get("DAN BACKS") +
                        metricHash.get("ED GIBBONS") +
                        metricHash.get("HAFSA GOMA") +
                        metricHash.get("SCOTT ROBB") +
                        metricHash.get("STEVE BERGERON") +
                        metricHash.get("MARK GRANDISH") +
                        metricHash.get("BRIAN KENDRICK") +
                        metricHash.get("KAREN LEIBOVICI") +
                        metricHash.get("CRIS BASUALDO") +
                        metricHash.get("JANE BATTY") +
                        metricHash.get("CARLA FROST") +
                        metricHash.get("JAMES JOHNSON") +
                        metricHash.get("BRYAN GEORGE KAPITZA") +
                        metricHash.get("LEE PERMANN") +
                        metricHash.get("ADIL PIRBHAI") +
                        metricHash.get("THOMAS ROBERTS") +
                        metricHash.get("TONY CATERINA") +
                        metricHash.get("SCOTT MCKEEN") +
                        metricHash.get("GRANT DAVID PULLISHY") +
                        metricHash.get("TERRY ROLLS") +
                        metricHash.get("BRENDAN VAN ALSTINE") +
                        metricHash.get("DUANE GOOD STRIKER") +
                        metricHash.get("BEN HENDERSON") +
                        metricHash.get("LORI JEFFERY-HEANEY") +
                        metricHash.get("SHEILA MCKAY") +
                        metricHash.get("HANA RAZGA") +
                        metricHash.get("BRYAN KENT ANDERSON") +
                        metricHash.get("RAMI BADER") +
                        metricHash.get("CALVIN LIM") +
                        metricHash.get("JENNIFER WATTS") +
                        metricHash.get("DON IVESON") +
                        metricHash.get("AL SLEMKO") +
                        metricHash.get("SHANE BERGDAHL") +
                        metricHash.get("KERRY DIOTTE") +
                        metricHash.get("VISHAL LUTHRA") +
                        metricHash.get("ROBERTO MAGLALANG") +
                        metricHash.get("CHINWE OKELU") +
                        metricHash.get("BRENT SCHAFFRICK") +
                        metricHash.get("VIKRAM BAGGA") +
                        metricHash.get("GERRY HORN") +
                        metricHash.get("CHUCK MCKENNA") +
                        metricHash.get("AMARJEET SOHI") +
                        metricHash.get("COUNCILLOR OVER VOTES") +
                        metricHash.get("COUNCILLOR UNDER VOTES"));
    metricHash.put("totalCouncilVotes", totalCouncilVotes);
    
    float totalCSBVotes = (metricHash.get("BECKY KALLAL") +
                        metricHash.get("ROMEO OCHOA") +
                        metricHash.get("PATRICIA GRELL") +
                        metricHash.get("LARRY KOWALCZYK") +
                        metricHash.get("CARLA SMILEY") +
                        metricHash.get("CYNTHIA BOSSMIN-DAVIS") +
                        metricHash.get("JENNIFER CHAHAL") +
                        metricHash.get("CINDY OLSEN") +
                        metricHash.get("DEBBIE ENGEL") +
                        metricHash.get("BRAD LOHNER") +
                        metricHash.get("JOHN ACHESON") +
                        metricHash.get("RUDY ARCILLA") +
                        metricHash.get("LAURI FRIESEN") +
                        metricHash.get("MARILYN BERGSTRA") +
                        metricHash.get("BEV HEISLER") +
                        metricHash.get("MICHAEL SAVARYN") +
                        metricHash.get("KATHERINE SCHWABENBAUER") +
                        metricHash.get("DANNY S. KINAL") +
                        metricHash.get("KARA PELECH") +
                        metricHash.get("LAURA THIBERT") +
                        metricHash.get("CATHOLIC SCHOOL BOARD TRUSTEE OVER VOTES") +
                        metricHash.get("CATHOLIC SCHOOL BOARD TRUSTEE UNDER VOTES"));
    metricHash.put("totalCSBVotes", totalCSBVotes);
    
    float totalPSBVotes = (metricHash.get("CHERYL JOHNER") +
                        metricHash.get("RORY JOHANNES KOOPMANS") +
                        metricHash.get("KEVAN WARNER") +
                        metricHash.get("AMANDA BEISIEGEL") +
                        metricHash.get("PERRY CHAHAL") +
                        metricHash.get("DARRYL LAGERQUIST") +
                        metricHash.get("GEORGE P. NICHOLSON") +
                        metricHash.get("KEN SHIPKA") +
                        metricHash.get("MARY GRADISAR") +
                        metricHash.get("TONY HENSHALL") +
                        metricHash.get("CHRISTOPHER SPENCER") +
                        metricHash.get("DAVE COLBURN") +
                        metricHash.get("NEIL MACDONALD") +
                        metricHash.get("HEATHER MACKENZIE") +
                        metricHash.get("KEN SOROKA") +
                        metricHash.get("MICHAEL JANZ") +
                        metricHash.get("JOANNA ROZMUS") +
                        metricHash.get("BEV SAWYER") +
                        metricHash.get("SARAH HOFFMAN") +
                        metricHash.get("GEORGE RICE") +
                        metricHash.get("CATHERINE RIPLEY") +
                        metricHash.get("LESLIE CLEARY") +
                        metricHash.get("TINA JARDINE") +
                        metricHash.get("BALRAJ MANHAS") +
                        metricHash.get("PUBLIC SCHOOL BOARD TRUSTEE OVER VOTES") +
                        metricHash.get("PUBLIC SCHOOL BOARD TRUSTEE UNDER VOTES"));
    metricHash.put("totalPSBVotes", totalPSBVotes);
    
    // voter turnout
    float eligibleVoters = (metricHash.get("eligibleVoters"));
    float voterTurnout = (totalMayorVotes == -9) ? -1 : totalMayorVotes / eligibleVoters * 100;
    metricHash.put("voterTurnout", voterTurnout);
    
    // eligible voter density
    float area = (metricHash.get("area"));
    float voterDensity = (eligibleVoters == -1) ? -1 : eligibleVoters / area;
    metricHash.put("voterDensity", voterDensity);    
    
    // CODE BELOW THIS POINT GENERATED WITH genMVCode.rb
    
    // candidate vote shares
    
    float bonarVotes = (metricHash.get("DARYL BONAR"));
    float bonarPercent = (bonarVotes == -1) ? -1 : bonarVotes / totalMayorVotes * 100;
    metricHash.put("bonarPercent", bonarPercent);

    float dorwardVotes = (metricHash.get("DAVID DORWARD"));
    float dorwardPercent = (dorwardVotes == -1) ? -1 : dorwardVotes / totalMayorVotes * 100;
    metricHash.put("dorwardPercent", dorwardPercent);

    float dowlingVotes = (metricHash.get("DAVE DOWLING"));
    float dowlingPercent = (dowlingVotes == -1) ? -1 : dowlingVotes / totalMayorVotes * 100;
    metricHash.put("dowlingPercent", dowlingPercent);

    float dromarskyVotes = (metricHash.get("DAN DROMARSKY"));
    float dromarskyPercent = (dromarskyVotes == -1) ? -1 : dromarskyVotes / totalMayorVotes * 100;
    metricHash.put("dromarskyPercent", dromarskyPercent);

    float ligertwoodVotes = (metricHash.get("BOB LIGERTWOOD"));
    float ligertwoodPercent = (ligertwoodVotes == -1) ? -1 : ligertwoodVotes / totalMayorVotes * 100;
    metricHash.put("ligertwoodPercent", ligertwoodPercent);

    float linekerVotes = (metricHash.get("ANDREW LINEKER"));
    float linekerPercent = (linekerVotes == -1) ? -1 : linekerVotes / totalMayorVotes * 100;
    metricHash.put("linekerPercent", linekerPercent);

    float mandelVotes = (metricHash.get("STEPHEN MANDEL"));
    float mandelPercent = (mandelVotes == -1) ? -1 : mandelVotes / totalMayorVotes * 100;
    metricHash.put("mandelPercent", mandelPercent);

    float knackVotes = (metricHash.get("ANDREW KNACK"));
    float knackPercent = (knackVotes == -1) ? -1 : knackVotes / totalCouncilVotes * 100;
    metricHash.put("knackPercent", knackPercent);

    float postVotes = (metricHash.get("JAMIE KENNETH POST"));
    float postPercent = (postVotes == -1) ? -1 : postVotes / totalCouncilVotes * 100;
    metricHash.put("postPercent", postPercent);

    float sloanVotes = (metricHash.get("LINDA SLOAN"));
    float sloanPercent = (sloanVotes == -1) ? -1 : sloanVotes / totalCouncilVotes * 100;
    metricHash.put("sloanPercent", sloanPercent);

    float hinderksVotes = (metricHash.get("THOMAS HINDERKS"));
    float hinderksPercent = (hinderksVotes == -1) ? -1 : hinderksVotes / totalCouncilVotes * 100;
    metricHash.put("hinderksPercent", hinderksPercent);

    float koziakVotes = (metricHash.get("DON KOZIAK"));
    float koziakPercent = (koziakVotes == -1) ? -1 : koziakVotes / totalCouncilVotes * 100;
    metricHash.put("koziakPercent", koziakPercent);

    float krushellVotes = (metricHash.get("KIM KRUSHELL"));
    float krushellPercent = (krushellVotes == -1) ? -1 : krushellVotes / totalCouncilVotes * 100;
    metricHash.put("krushellPercent", krushellPercent);

    float maloneRichardsVotes = (metricHash.get("ROXIE MALONE-RICHARDS"));
    float maloneRichardsPercent = (maloneRichardsVotes == -1) ? -1 : maloneRichardsVotes / totalCouncilVotes * 100;
    metricHash.put("maloneRichardsPercent", maloneRichardsPercent);

    float tupperVotes = (metricHash.get("SHELLEY TUPPER"));
    float tupperPercent = (tupperVotes == -1) ? -1 : tupperVotes / totalCouncilVotes * 100;
    metricHash.put("tupperPercent", tupperPercent);

    float waddyVotes = (metricHash.get("MICHAEL WADDY"));
    float waddyPercent = (waddyVotes == -1) ? -1 : waddyVotes / totalCouncilVotes * 100;
    metricHash.put("waddyPercent", waddyPercent);

    float cassadyVotes = (metricHash.get("KIM CASSADY"));
    float cassadyPercent = (cassadyVotes == -1) ? -1 : cassadyVotes / totalCouncilVotes * 100;
    metricHash.put("cassadyPercent", cassadyPercent);

    float demersVotes = (metricHash.get("TERRY DEMERS"));
    float demersPercent = (demersVotes == -1) ? -1 : demersVotes / totalCouncilVotes * 100;
    metricHash.put("demersPercent", demersPercent);

    float fairbridgeVotes = (metricHash.get("SHAWN PHILIP FAIRBRIDGE"));
    float fairbridgePercent = (fairbridgeVotes == -1) ? -1 : fairbridgeVotes / totalCouncilVotes * 100;
    metricHash.put("fairbridgePercent", fairbridgePercent);

    float lokenVotes = (metricHash.get("DAVE LOKEN"));
    float lokenPercent = (lokenVotes == -1) ? -1 : lokenVotes / totalCouncilVotes * 100;
    metricHash.put("lokenPercent", lokenPercent);

    float naboulsiVotes = (metricHash.get("HATEM NABOULSI"));
    float naboulsiPercent = (naboulsiVotes == -1) ? -1 : naboulsiVotes / totalCouncilVotes * 100;
    metricHash.put("naboulsiPercent", naboulsiPercent);

    float oplanichVotes = (metricHash.get("JOHN (GIOVANNI) OPLANICH"));
    float oplanichPercent = (oplanichVotes == -1) ? -1 : oplanichVotes / totalCouncilVotes * 100;
    metricHash.put("oplanichPercent", oplanichPercent);

    float siverVotes = (metricHash.get("GREG SIVER"));
    float siverPercent = (siverVotes == -1) ? -1 : siverVotes / totalCouncilVotes * 100;
    metricHash.put("siverPercent", siverPercent);

    float sobolewskiVotes = (metricHash.get("LOUIS SOBOLEWSKI"));
    float sobolewskiPercent = (sobolewskiVotes == -1) ? -1 : sobolewskiVotes / totalCouncilVotes * 100;
    metricHash.put("sobolewskiPercent", sobolewskiPercent);

    float suessVotes = (metricHash.get("MICHEAL SUESS"));
    float suessPercent = (suessVotes == -1) ? -1 : suessVotes / totalCouncilVotes * 100;
    metricHash.put("suessPercent", suessPercent);

    float atkinsonVotes = (metricHash.get("KEN ATKINSON"));
    float atkinsonPercent = (atkinsonVotes == -1) ? -1 : atkinsonVotes / totalCouncilVotes * 100;
    metricHash.put("atkinsonPercent", atkinsonPercent);

    float backsVotes = (metricHash.get("DAN BACKS"));
    float backsPercent = (backsVotes == -1) ? -1 : backsVotes / totalCouncilVotes * 100;
    metricHash.put("backsPercent", backsPercent);

    float gibbonsVotes = (metricHash.get("ED GIBBONS"));
    float gibbonsPercent = (gibbonsVotes == -1) ? -1 : gibbonsVotes / totalCouncilVotes * 100;
    metricHash.put("gibbonsPercent", gibbonsPercent);

    float gomaVotes = (metricHash.get("HAFSA GOMA"));
    float gomaPercent = (gomaVotes == -1) ? -1 : gomaVotes / totalCouncilVotes * 100;
    metricHash.put("gomaPercent", gomaPercent);

    float robbVotes = (metricHash.get("SCOTT ROBB"));
    float robbPercent = (robbVotes == -1) ? -1 : robbVotes / totalCouncilVotes * 100;
    metricHash.put("robbPercent", robbPercent);

    float bergeronVotes = (metricHash.get("STEVE BERGERON"));
    float bergeronPercent = (bergeronVotes == -1) ? -1 : bergeronVotes / totalCouncilVotes * 100;
    metricHash.put("bergeronPercent", bergeronPercent);

    float grandishVotes = (metricHash.get("MARK GRANDISH"));
    float grandishPercent = (grandishVotes == -1) ? -1 : grandishVotes / totalCouncilVotes * 100;
    metricHash.put("grandishPercent", grandishPercent);

    float kendrickVotes = (metricHash.get("BRIAN KENDRICK"));
    float kendrickPercent = (kendrickVotes == -1) ? -1 : kendrickVotes / totalCouncilVotes * 100;
    metricHash.put("kendrickPercent", kendrickPercent);

    float leiboviciVotes = (metricHash.get("KAREN LEIBOVICI"));
    float leiboviciPercent = (leiboviciVotes == -1) ? -1 : leiboviciVotes / totalCouncilVotes * 100;
    metricHash.put("leiboviciPercent", leiboviciPercent);

    float basualdoVotes = (metricHash.get("CRIS BASUALDO"));
    float basualdoPercent = (basualdoVotes == -1) ? -1 : basualdoVotes / totalCouncilVotes * 100;
    metricHash.put("basualdoPercent", basualdoPercent);

    float battyVotes = (metricHash.get("JANE BATTY"));
    float battyPercent = (battyVotes == -1) ? -1 : battyVotes / totalCouncilVotes * 100;
    metricHash.put("battyPercent", battyPercent);

    float frostVotes = (metricHash.get("CARLA FROST"));
    float frostPercent = (frostVotes == -1) ? -1 : frostVotes / totalCouncilVotes * 100;
    metricHash.put("frostPercent", frostPercent);

    float johnsonVotes = (metricHash.get("JAMES JOHNSON"));
    float johnsonPercent = (johnsonVotes == -1) ? -1 : johnsonVotes / totalCouncilVotes * 100;
    metricHash.put("johnsonPercent", johnsonPercent);

    float kapitzaVotes = (metricHash.get("BRYAN GEORGE KAPITZA"));
    float kapitzaPercent = (kapitzaVotes == -1) ? -1 : kapitzaVotes / totalCouncilVotes * 100;
    metricHash.put("kapitzaPercent", kapitzaPercent);

    float permannVotes = (metricHash.get("LEE PERMANN"));
    float permannPercent = (permannVotes == -1) ? -1 : permannVotes / totalCouncilVotes * 100;
    metricHash.put("permannPercent", permannPercent);

    float pirbhaiVotes = (metricHash.get("ADIL PIRBHAI"));
    float pirbhaiPercent = (pirbhaiVotes == -1) ? -1 : pirbhaiVotes / totalCouncilVotes * 100;
    metricHash.put("pirbhaiPercent", pirbhaiPercent);

    float robertsVotes = (metricHash.get("THOMAS ROBERTS"));
    float robertsPercent = (robertsVotes == -1) ? -1 : robertsVotes / totalCouncilVotes * 100;
    metricHash.put("robertsPercent", robertsPercent);

    float caterinaVotes = (metricHash.get("TONY CATERINA"));
    float caterinaPercent = (caterinaVotes == -1) ? -1 : caterinaVotes / totalCouncilVotes * 100;
    metricHash.put("caterinaPercent", caterinaPercent);

    float mckeenVotes = (metricHash.get("SCOTT MCKEEN"));
    float mckeenPercent = (mckeenVotes == -1) ? -1 : mckeenVotes / totalCouncilVotes * 100;
    metricHash.put("mckeenPercent", mckeenPercent);

    float pullishyVotes = (metricHash.get("GRANT DAVID PULLISHY"));
    float pullishyPercent = (pullishyVotes == -1) ? -1 : pullishyVotes / totalCouncilVotes * 100;
    metricHash.put("pullishyPercent", pullishyPercent);

    float rollsVotes = (metricHash.get("TERRY ROLLS"));
    float rollsPercent = (rollsVotes == -1) ? -1 : rollsVotes / totalCouncilVotes * 100;
    metricHash.put("rollsPercent", rollsPercent);

    float alstineVotes = (metricHash.get("BRENDAN VAN ALSTINE"));
    float alstinePercent = (alstineVotes == -1) ? -1 : alstineVotes / totalCouncilVotes * 100;
    metricHash.put("alstinePercent", alstinePercent);

    float strikerVotes = (metricHash.get("DUANE GOOD STRIKER"));
    float strikerPercent = (strikerVotes == -1) ? -1 : strikerVotes / totalCouncilVotes * 100;
    metricHash.put("strikerPercent", strikerPercent);

    float hendersonVotes = (metricHash.get("BEN HENDERSON"));
    float hendersonPercent = (hendersonVotes == -1) ? -1 : hendersonVotes / totalCouncilVotes * 100;
    metricHash.put("hendersonPercent", hendersonPercent);

    float jefferyHeaneyVotes = (metricHash.get("LORI JEFFERY-HEANEY"));
    float jefferyHeaneyPercent = (jefferyHeaneyVotes == -1) ? -1 : jefferyHeaneyVotes / totalCouncilVotes * 100;
    metricHash.put("jefferyHeaneyPercent", jefferyHeaneyPercent);

    float mckayVotes = (metricHash.get("SHEILA MCKAY"));
    float mckayPercent = (mckayVotes == -1) ? -1 : mckayVotes / totalCouncilVotes * 100;
    metricHash.put("mckayPercent", mckayPercent);

    float razgaVotes = (metricHash.get("HANA RAZGA"));
    float razgaPercent = (razgaVotes == -1) ? -1 : razgaVotes / totalCouncilVotes * 100;
    metricHash.put("razgaPercent", razgaPercent);

    float andersonVotes = (metricHash.get("BRYAN KENT ANDERSON"));
    float andersonPercent = (andersonVotes == -1) ? -1 : andersonVotes / totalCouncilVotes * 100;
    metricHash.put("andersonPercent", andersonPercent);

    float baderVotes = (metricHash.get("RAMI BADER"));
    float baderPercent = (baderVotes == -1) ? -1 : baderVotes / totalCouncilVotes * 100;
    metricHash.put("baderPercent", baderPercent);

    float limVotes = (metricHash.get("CALVIN LIM"));
    float limPercent = (limVotes == -1) ? -1 : limVotes / totalCouncilVotes * 100;
    metricHash.put("limPercent", limPercent);

    float wattsVotes = (metricHash.get("JENNIFER WATTS"));
    float wattsPercent = (wattsVotes == -1) ? -1 : wattsVotes / totalCouncilVotes * 100;
    metricHash.put("wattsPercent", wattsPercent);

    float ivesonVotes = (metricHash.get("DON IVESON"));
    float ivesonPercent = (ivesonVotes == -1) ? -1 : ivesonVotes / totalCouncilVotes * 100;
    metricHash.put("ivesonPercent", ivesonPercent);

    float slemkoVotes = (metricHash.get("AL SLEMKO"));
    float slemkoPercent = (slemkoVotes == -1) ? -1 : slemkoVotes / totalCouncilVotes * 100;
    metricHash.put("slemkoPercent", slemkoPercent);

    float bergdahlVotes = (metricHash.get("SHANE BERGDAHL"));
    float bergdahlPercent = (bergdahlVotes == -1) ? -1 : bergdahlVotes / totalCouncilVotes * 100;
    metricHash.put("bergdahlPercent", bergdahlPercent);

    float diotteVotes = (metricHash.get("KERRY DIOTTE"));
    float diottePercent = (diotteVotes == -1) ? -1 : diotteVotes / totalCouncilVotes * 100;
    metricHash.put("diottePercent", diottePercent);

    float luthraVotes = (metricHash.get("VISHAL LUTHRA"));
    float luthraPercent = (luthraVotes == -1) ? -1 : luthraVotes / totalCouncilVotes * 100;
    metricHash.put("luthraPercent", luthraPercent);

    float maglalangVotes = (metricHash.get("ROBERTO MAGLALANG"));
    float maglalangPercent = (maglalangVotes == -1) ? -1 : maglalangVotes / totalCouncilVotes * 100;
    metricHash.put("maglalangPercent", maglalangPercent);

    float okeluVotes = (metricHash.get("CHINWE OKELU"));
    float okeluPercent = (okeluVotes == -1) ? -1 : okeluVotes / totalCouncilVotes * 100;
    metricHash.put("okeluPercent", okeluPercent);

    float schaffrickVotes = (metricHash.get("BRENT SCHAFFRICK"));
    float schaffrickPercent = (schaffrickVotes == -1) ? -1 : schaffrickVotes / totalCouncilVotes * 100;
    metricHash.put("schaffrickPercent", schaffrickPercent);

    float baggaVotes = (metricHash.get("VIKRAM BAGGA"));
    float baggaPercent = (baggaVotes == -1) ? -1 : baggaVotes / totalCouncilVotes * 100;
    metricHash.put("baggaPercent", baggaPercent);

    float hornVotes = (metricHash.get("GERRY HORN"));
    float hornPercent = (hornVotes == -1) ? -1 : hornVotes / totalCouncilVotes * 100;
    metricHash.put("hornPercent", hornPercent);

    float mckennaVotes = (metricHash.get("CHUCK MCKENNA"));
    float mckennaPercent = (mckennaVotes == -1) ? -1 : mckennaVotes / totalCouncilVotes * 100;
    metricHash.put("mckennaPercent", mckennaPercent);

    float sohiVotes = (metricHash.get("AMARJEET SOHI"));
    float sohiPercent = (sohiVotes == -1) ? -1 : sohiVotes / totalCouncilVotes * 100;
    metricHash.put("sohiPercent", sohiPercent);

    float kallalVotes = (metricHash.get("BECKY KALLAL"));
    float kallalPercent = (kallalVotes == -1) ? -1 : kallalVotes / totalCSBVotes * 100;
    metricHash.put("kallalPercent", kallalPercent);

    float ochoaVotes = (metricHash.get("ROMEO OCHOA"));
    float ochoaPercent = (ochoaVotes == -1) ? -1 : ochoaVotes / totalCSBVotes * 100;
    metricHash.put("ochoaPercent", ochoaPercent);

    float grellVotes = (metricHash.get("PATRICIA GRELL"));
    float grellPercent = (grellVotes == -1) ? -1 : grellVotes / totalCSBVotes * 100;
    metricHash.put("grellPercent", grellPercent);

    float kowalczykVotes = (metricHash.get("LARRY KOWALCZYK"));
    float kowalczykPercent = (kowalczykVotes == -1) ? -1 : kowalczykVotes / totalCSBVotes * 100;
    metricHash.put("kowalczykPercent", kowalczykPercent);

    float smileyVotes = (metricHash.get("CARLA SMILEY"));
    float smileyPercent = (smileyVotes == -1) ? -1 : smileyVotes / totalCSBVotes * 100;
    metricHash.put("smileyPercent", smileyPercent);

    float bossminDavisVotes = (metricHash.get("CYNTHIA BOSSMIN-DAVIS"));
    float bossminDavisPercent = (bossminDavisVotes == -1) ? -1 : bossminDavisVotes / totalCSBVotes * 100;
    metricHash.put("bossminDavisPercent", bossminDavisPercent);

    float chahalVotes = (metricHash.get("JENNIFER CHAHAL"));
    float chahalPercent = (chahalVotes == -1) ? -1 : chahalVotes / totalCSBVotes * 100;
    metricHash.put("chahalPercent", chahalPercent);

    float olsenVotes = (metricHash.get("CINDY OLSEN"));
    float olsenPercent = (olsenVotes == -1) ? -1 : olsenVotes / totalCSBVotes * 100;
    metricHash.put("olsenPercent", olsenPercent);

    float engelVotes = (metricHash.get("DEBBIE ENGEL"));
    float engelPercent = (engelVotes == -1) ? -1 : engelVotes / totalCSBVotes * 100;
    metricHash.put("engelPercent", engelPercent);

    float lohnerVotes = (metricHash.get("BRAD LOHNER"));
    float lohnerPercent = (lohnerVotes == -1) ? -1 : lohnerVotes / totalCSBVotes * 100;
    metricHash.put("lohnerPercent", lohnerPercent);

    float achesonVotes = (metricHash.get("JOHN ACHESON"));
    float achesonPercent = (achesonVotes == -1) ? -1 : achesonVotes / totalCSBVotes * 100;
    metricHash.put("achesonPercent", achesonPercent);

    float arcillaVotes = (metricHash.get("RUDY ARCILLA"));
    float arcillaPercent = (arcillaVotes == -1) ? -1 : arcillaVotes / totalCSBVotes * 100;
    metricHash.put("arcillaPercent", arcillaPercent);

    float friesenVotes = (metricHash.get("LAURI FRIESEN"));
    float friesenPercent = (friesenVotes == -1) ? -1 : friesenVotes / totalCSBVotes * 100;
    metricHash.put("friesenPercent", friesenPercent);

    float bergstraVotes = (metricHash.get("MARILYN BERGSTRA"));
    float bergstraPercent = (bergstraVotes == -1) ? -1 : bergstraVotes / totalCSBVotes * 100;
    metricHash.put("bergstraPercent", bergstraPercent);

    float heislerVotes = (metricHash.get("BEV HEISLER"));
    float heislerPercent = (heislerVotes == -1) ? -1 : heislerVotes / totalCSBVotes * 100;
    metricHash.put("heislerPercent", heislerPercent);

    float savarynVotes = (metricHash.get("MICHAEL SAVARYN"));
    float savarynPercent = (savarynVotes == -1) ? -1 : savarynVotes / totalCSBVotes * 100;
    metricHash.put("savarynPercent", savarynPercent);

    float schwabenbauerVotes = (metricHash.get("KATHERINE SCHWABENBAUER"));
    float schwabenbauerPercent = (schwabenbauerVotes == -1) ? -1 : schwabenbauerVotes / totalCSBVotes * 100;
    metricHash.put("schwabenbauerPercent", schwabenbauerPercent);

    float kinalVotes = (metricHash.get("DANNY S. KINAL"));
    float kinalPercent = (kinalVotes == -1) ? -1 : kinalVotes / totalCSBVotes * 100;
    metricHash.put("kinalPercent", kinalPercent);

    float pelechVotes = (metricHash.get("KARA PELECH"));
    float pelechPercent = (pelechVotes == -1) ? -1 : pelechVotes / totalCSBVotes * 100;
    metricHash.put("pelechPercent", pelechPercent);

    float thibertVotes = (metricHash.get("LAURA THIBERT"));
    float thibertPercent = (thibertVotes == -1) ? -1 : thibertVotes / totalCSBVotes * 100;
    metricHash.put("thibertPercent", thibertPercent);

    float johnerVotes = (metricHash.get("CHERYL JOHNER"));
    float johnerPercent = (johnerVotes == -1) ? -1 : johnerVotes / totalPSBVotes * 100;
    metricHash.put("johnerPercent", johnerPercent);

    float koopmansVotes = (metricHash.get("RORY JOHANNES KOOPMANS"));
    float koopmansPercent = (koopmansVotes == -1) ? -1 : koopmansVotes / totalPSBVotes * 100;
    metricHash.put("koopmansPercent", koopmansPercent);

    float warnerVotes = (metricHash.get("KEVAN WARNER"));
    float warnerPercent = (warnerVotes == -1) ? -1 : warnerVotes / totalPSBVotes * 100;
    metricHash.put("warnerPercent", warnerPercent);

    float beisiegelVotes = (metricHash.get("AMANDA BEISIEGEL"));
    float beisiegelPercent = (beisiegelVotes == -1) ? -1 : beisiegelVotes / totalPSBVotes * 100;
    metricHash.put("beisiegelPercent", beisiegelPercent);

    float chahal_1Votes = (metricHash.get("PERRY CHAHAL"));
    float chahal_1Percent = (chahal_1Votes == -1) ? -1 : chahal_1Votes / totalPSBVotes * 100;
    metricHash.put("chahal_1Percent", chahal_1Percent);

    float lagerquistVotes = (metricHash.get("DARRYL LAGERQUIST"));
    float lagerquistPercent = (lagerquistVotes == -1) ? -1 : lagerquistVotes / totalPSBVotes * 100;
    metricHash.put("lagerquistPercent", lagerquistPercent);

    float nicholsonVotes = (metricHash.get("GEORGE P. NICHOLSON"));
    float nicholsonPercent = (nicholsonVotes == -1) ? -1 : nicholsonVotes / totalPSBVotes * 100;
    metricHash.put("nicholsonPercent", nicholsonPercent);

    float shipkaVotes = (metricHash.get("KEN SHIPKA"));
    float shipkaPercent = (shipkaVotes == -1) ? -1 : shipkaVotes / totalPSBVotes * 100;
    metricHash.put("shipkaPercent", shipkaPercent);

    float gradisarVotes = (metricHash.get("MARY GRADISAR"));
    float gradisarPercent = (gradisarVotes == -1) ? -1 : gradisarVotes / totalPSBVotes * 100;
    metricHash.put("gradisarPercent", gradisarPercent);

    float henshallVotes = (metricHash.get("TONY HENSHALL"));
    float henshallPercent = (henshallVotes == -1) ? -1 : henshallVotes / totalPSBVotes * 100;
    metricHash.put("henshallPercent", henshallPercent);

    float spencerVotes = (metricHash.get("CHRISTOPHER SPENCER"));
    float spencerPercent = (spencerVotes == -1) ? -1 : spencerVotes / totalPSBVotes * 100;
    metricHash.put("spencerPercent", spencerPercent);

    float colburnVotes = (metricHash.get("DAVE COLBURN"));
    float colburnPercent = (colburnVotes == -1) ? -1 : colburnVotes / totalPSBVotes * 100;
    metricHash.put("colburnPercent", colburnPercent);

    float macdonaldVotes = (metricHash.get("NEIL MACDONALD"));
    float macdonaldPercent = (macdonaldVotes == -1) ? -1 : macdonaldVotes / totalPSBVotes * 100;
    metricHash.put("macdonaldPercent", macdonaldPercent);

    float mackenzieVotes = (metricHash.get("HEATHER MACKENZIE"));
    float mackenziePercent = (mackenzieVotes == -1) ? -1 : mackenzieVotes / totalPSBVotes * 100;
    metricHash.put("mackenziePercent", mackenziePercent);

    float sorokaVotes = (metricHash.get("KEN SOROKA"));
    float sorokaPercent = (sorokaVotes == -1) ? -1 : sorokaVotes / totalPSBVotes * 100;
    metricHash.put("sorokaPercent", sorokaPercent);

    float janzVotes = (metricHash.get("MICHAEL JANZ"));
    float janzPercent = (janzVotes == -1) ? -1 : janzVotes / totalPSBVotes * 100;
    metricHash.put("janzPercent", janzPercent);

    float rozmusVotes = (metricHash.get("JOANNA ROZMUS"));
    float rozmusPercent = (rozmusVotes == -1) ? -1 : rozmusVotes / totalPSBVotes * 100;
    metricHash.put("rozmusPercent", rozmusPercent);

    float sawyerVotes = (metricHash.get("BEV SAWYER"));
    float sawyerPercent = (sawyerVotes == -1) ? -1 : sawyerVotes / totalPSBVotes * 100;
    metricHash.put("sawyerPercent", sawyerPercent);

    float hoffmanVotes = (metricHash.get("SARAH HOFFMAN"));
    float hoffmanPercent = (hoffmanVotes == -1) ? -1 : hoffmanVotes / totalPSBVotes * 100;
    metricHash.put("hoffmanPercent", hoffmanPercent);

    float riceVotes = (metricHash.get("GEORGE RICE"));
    float ricePercent = (riceVotes == -1) ? -1 : riceVotes / totalPSBVotes * 100;
    metricHash.put("ricePercent", ricePercent);

    float ripleyVotes = (metricHash.get("CATHERINE RIPLEY"));
    float ripleyPercent = (ripleyVotes == -1) ? -1 : ripleyVotes / totalPSBVotes * 100;
    metricHash.put("ripleyPercent", ripleyPercent);

    float clearyVotes = (metricHash.get("LESLIE CLEARY"));
    float clearyPercent = (clearyVotes == -1) ? -1 : clearyVotes / totalPSBVotes * 100;
    metricHash.put("clearyPercent", clearyPercent);

    float jardineVotes = (metricHash.get("TINA JARDINE"));
    float jardinePercent = (jardineVotes == -1) ? -1 : jardineVotes / totalPSBVotes * 100;
    metricHash.put("jardinePercent", jardinePercent);

    float manhasVotes = (metricHash.get("BALRAJ MANHAS"));
    float manhasPercent = (manhasVotes == -1) ? -1 : manhasVotes / totalPSBVotes * 100;
    metricHash.put("manhasPercent", manhasPercent);


    
  } // genCalculatedMetrics method
  
  
  // ------------------------------------------------------
  // draw
  // called during each iteration of draw loop
  // ------------------------------------------------------
  void draw(DModeManager dModeMan) {
    
    // declare variables
    PVector[] polyScreen;
    
    // iterate through polygons
    for(int i = 0; i < coords.size(); i++) {
      
      // get polygon 
      polyScreen = (PVector[]) coordsScreen.get(i);

      // draw polygon
      setColour(dModeMan);
      beginShape();
      
      for(int j = 0; j < polyScreen.length; j++) {

        vertex(polyScreen[j].x, polyScreen[j].y);
        
        // DEBUG -- PLOT DATA POINTS
//        stroke(0, 100); ////////////////////////
//        point(polyScreen[j].x, polyScreen[j].y); ///////////////////
//        stroke(50, 50, 50, 75); ////////////////
        
      } // for loop j
      
      endShape(CLOSE);
      
    } // for loop i
      
  } // draw method
  
  // -------------------------------------------------
  // setColour
  // retrieves fill and stroke colours based on drawing mode
  // -------------------------------------------------
  void setColour(DModeManager dModeMan) {
    
    DMode dMode = dModeMan.getDMode();
    
    // check if drawing mode has metric; if so, retrieve colour and set;
    // if not, set colour to default value
    if(dMode.hasMetric) {
      
      // retrieve metric value
      float metricVal = (Float) metricHash.get(dMode.hashName);
      
      // check if metric has no data flag; if so, set colours to "no data" value;
      // if not, retrieve drawing mode colour and set
      if(metricVal == -1) { 
        fill(noDataFil);
        stroke(strok);
      } else {
        color col = dMode.getColour(metricVal);
        fill(col);
        stroke(strok);
      } // if
    } else {
      fill(dFil);
      stroke(strok);
    } // if 

  } // setColour method
  
  // -----------------------------------------
  // douglasPeucker
  // this method is used to remove unnecessary points
  // from the plot, using the Douglas-Peucker algorithm
  // -----------------------------------------
  PVector[] douglasPeucker (PVector[] points, float epsilon) {
    
    // declare variables
    float dmax, d;
    int index;
    PVector[] points1, points2, out;
    
    // calculate point with maximum orthogonal distance from line
    // between first and last point in "points"
    dmax = 0;
    index = 0;
    
    for(int i = 1; i < (points.length - 1); i++) {
      
      // check if first and last point are coincident (which will be the case for closed loops)
      // if so, use absolute distance from the coincident points in place of orhotgonal distance
      if(((points[0].x - points[points.length - 1].x) == 0) && ((points[0].y - points[points.length - 1].y) == 0)) {
        d = sqrt(sq(points[i].x - points[0].x) + sq(points[i].y - points[0].y));
      } else {
        // calculate orthogonal distance; store largest distance and index of point
        d = orthogonalDistance(points[i], points[0], points[points.length - 1]);
      }
      if(d > dmax) {
        dmax = d;
        index = i;
      } // if
    } // for
    
    // if dmax is greather than epsilon (tunable parameter), divide points into arrays
    // at location of dmax and recursively call method on both arrays
    if(dmax >= epsilon) {
      points1 = douglasPeucker(((PVector[]) subset(points, 0, (index + 1))), epsilon);
      points2 = douglasPeucker(((PVector[]) subset(points, index, (points.length - index))), epsilon);
      
      // store points calculate above in output variable
      out = (PVector[]) concat(shorten(points1), points2);
    } else {
      // if no points fall outside dmax, return only first and last points in "points"
      out = new PVector[2];
      out[0] = points[0];
      out[1] = points[points.length - 1];
    } // if
    
    // return output variable
    return out;
    
  } // douglasPeucker algorithm
  
  // ------------------------------------
  // orthogonalDistance
  // calculates orthogonal distance between a line
  // and a point. Used for douglasPeucker method above
  // ------------------------------------
  float orthogonalDistance(PVector poin, PVector firstPoint, PVector lastPoint) {
    
    // declare variables
    float out;
    //float[] v1, v2;
    float dotProd, l1, l2, sinTheta;
    
    // convert lat/long to pixel coordinates
    
    // calculate dot product of vectors between firstPoint and poin (v1) and
    // firstPoint and lastPoint (v2)
    PVector v2 = PVector.sub(lastPoint, firstPoint);
    PVector v1 = PVector.sub(poin, firstPoint);
    dotProd = v1.dot(v2);

    // calculate lengths between firstPoint and poin (l1) and firstPoint and
    // lastPoint (l2)
    l1 = v1.mag();
    l2 = v2.mag();
    
    // calculate sin of angle between v1 and v2. if dotProd = 0, set sinTheta to 0
    if(dotProd == 0) {
      sinTheta = 0;
    } else {
      sinTheta = sqrt(1 - sq(dotProd / (l1 * l2)));
    } // if
    // if sinTheta very small, set to 0
    // this is a bit of a hack
    if(sinTheta < 0.001) {
      sinTheta = 0;
    } // if
    
    // calculate distance and return;
    out = l1 * sinTheta;
    return out;    
    
  } // orthogonalDistance
  
  // ---------------------------------------
  // checkMouseOver
  // checks if mouse is positioned over neighbourhood
  // if so, displays neighbourhood name, description, and
  // current metric value
  // ---------------------------------------
  void checkMouseOver(PVector poin, NHoodInfoUI nhui, DModeManager dModeMan) {
    
    // iterate through neighbourhood's polygons and check each
    for(int i = 0; i < coords.size(); i++) {
      
      PVector[] polyScreen;
      String sCurVal = "";
      float curVal = -2;
      DMode dMode = dModeMan.getDMode();
      
      // get polygon and remove last (duplicate) coordinate
      polyScreen = (PVector[]) coordsScreen.get(i);
      polyScreen = (PVector[]) shorten(polyScreen);      
      
      // test polygon; if mouse is inside, update neighbourhood information
      if(pointInPolygon(polyScreen, poin)) {
        
        // check if drawing mode has metric; if not, set curVal to "blank flag" value;
        // if so, retrieve current metric value and store in curVal
        if(dMode.hasMetric) {
          curVal = (Float) metricHash.get(dMode.hashName);
        } else {
          curVal = -2;
        } // if
        
        // check for "no data" flag, or 
        // flag indicating field should be left blank
        if(curVal == -1) {
          sCurVal = "no data";
        } else if(curVal == -2) {
          sCurVal = "";
        } else {
          sCurVal = nf(curVal, 3, 1);
        } // if
        
        // update neighbourhood information
        nhui.drawDynamic(name, sCurVal);
        
      } // if
      
    } // for
    
  } //checkMouseOver
  
  // ----------------------------------------
  // pointInPolygon
  // checks if inputted point is within polygon;
  // returns true if so, false if not
  // ----------------------------------------
  boolean pointInPolygon(PVector[] poly, PVector poin) {
    
    int i;
    int j = poly.length - 1;
    boolean oddNodes = false;
    
    for(i = 0; i < poly.length; i++) {
      
      if(((poly[i].y < poin.y) && (poly[j].y >= poin.y)) ||
          ((poly[j].y < poin.y) && (poly[i].y >= poin.y))) {
            if((poly[i].x + (poin.y - poly[i].y) / (poly[j].y - poly[i].y) * (poly[j].x - poly[i].x)) < poin.x) {
            if(oddNodes) {
              oddNodes = false;
            } else {
              oddNodes = true;
            } // if
          } // if
        } // if
        
        j = i;
    } // for
    
    return oddNodes;    
    
  } // pointInPolygon
  
  // ----------------------------------------
  // calculatePolygonArea
  // note: assumes that input polygon coordinates are in metres
  // also note: assumes input polygons are traced counter-clockwise
  // this is based on an algorithm written in C by Darel Rex Finlay
  // see http://www.alienryderflex.com/polygon_area/
  // ----------------------------------------
  float calculatePolygonArea(PVector[] poly) {
  
    float out = 0;
    int j = 0;
    
    // iterate through polygon segments and calculate the area
    // between each segment and y-axis
    for(int i = 0; i < poly.length; i++) {
      
      j += 1;
      if(j==poly.length) j=0;
      
      out += (poly[i].x + poly[j].x) * (poly[j].y - poly[i].y);
    } // for
        
    // convert area from square metres to square kilometres and return
    return out/1000000;
    
  } // calculatePolygonArea
  
} // class

